﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling.Primitives;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components
{
    /// <summary>
    /// Тип внутреннего соединителя.
    /// </summary>
    public enum StickConnectorType
    {
        /// <summary>
        /// Соединитель существует физически, принадлежит профилю, находится на его боковой стороне.
        /// </summary>
        BelongToShpross, 
        /// <summary>
        /// Соединитель виртуальный, используется для соединения профилей друг с другом, когда конец одного профиля присоединяется к боковой
        /// стороне другого.
        /// </summary>
        Virtual
    }
    [Serializable]
    public class StickConnector : Connector, ICoveredByCap
    {
        private StickConnectorType stickConnectorType;

        public StickConnectorType StickConnectorType
        {
            get { return stickConnectorType; }
            set { stickConnectorType = value; }
        }
        

        private StickConnectionType sticked = StickConnectionType.None;

        public StickConnectionType Sticked
        {
            get { return sticked; }
            set { sticked = value; }
        }

        public StickConnector()
        {
            this.tolerance = 30;
        }

        public override void AcceptVisitor(IModelVisitor visitor)
        {
            visitor.Visit(this);
        }

        public override ModelPoint GetNearestPointOnComponent(ModelPoint point, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            sticked = StickConnectionType.Auto;
            return (ModelPoint)this.StartPoint.Clone();
        }

        public override IComponent ConnectTo(IComponent component)
        {
            return base.ConnectTo(component);
        }

        public override IComponent ConnectTo(IComponent component, ITAP.Core.Modelling.Primitives.EndType endForConnect)
        {
            List<IComponent> validComponentsToConnect = new List<IComponent>();
            this.RecursiveConnectTo(component, validComponentsToConnect);

            double distance = double.MaxValue;
            double currentDistance = double.MaxValue;
            IComponent componentToConnect = null;

            //   ModelPoint nearestPointOnComponent = null;
            ModelPoint currentPoint = null;

            StickConnectionType sticked;
            foreach (IComponent validComponent in validComponentsToConnect)
            {
                switch (endForConnect)
                {
                    case EndType.Start:
                        currentPoint = validComponent.GetNearestPointOnComponent(this.StartPoint, this.primitives[0].GetPoint(0.01), StickConnectionType.Auto, out sticked, ProjectionType.Intersection);
                        break;
                    case EndType.End:
                        currentPoint = validComponent.GetNearestPointOnComponent(this.EndPoint, this.primitives[0].GetPoint(0.99), StickConnectionType.Auto, out sticked, ProjectionType.Intersection);
                        break;
                }
                if (currentPoint == null)
                    continue;

                if (endForConnect == EndType.Start)
                    currentDistance = ModelCalculations.GetDistance(currentPoint, this.StartPoint);
                else if (endForConnect == EndType.End)
                    currentDistance = ModelCalculations.GetDistance(currentPoint, this.EndPoint);
                else
                    throw new Exception();
                if (distance > currentDistance)
                {
                    distance = currentDistance;
                    componentToConnect = validComponent;
                }
            }

            if (componentToConnect == null)
                return null;

            if (!this.ConnectedComponents.Contains(componentToConnect))
            {
                this.ConnectedComponents.Add(componentToConnect);
                switch (endForConnect)
                {
                    case EndType.Start:
                        this.StartConnectedComponent = componentToConnect;
                        break;
                    case EndType.End:
                        this.EndConnectedComponent = componentToConnect;
                        break;
                }
                return this;
            }
            else
                return null;

//            return base.ConnectTo(component, endForConnect);
        }

        public override IComponent ConnectToEnded(IEndedComponent endedComponent, ITAP.Core.Modelling.Primitives.EndType endToConnect)
        {
            return base.ConnectToEnded(endedComponent, endToConnect);
        }

        #region ICoveredByCap Members
        public SnapOnCap SnapOnCap
        {
            get 
            {
                foreach (IComponent innerComponent in this.InnerComponents)
                    if (innerComponent is SnapOnCap)
                        return (SnapOnCap)innerComponent;

                return null;
            }
        }

        #endregion

        public override bool IsInTolerance(ModelPoint point)
        {
            if (ModelCalculations.GetDistance(this.StartPoint, point) < this.tolerance)
                return true;
            else
                return false;
        }

        public override bool IsInTolerance(ModelPoint point, double tolerance)
        {
            if (ModelCalculations.GetDistance(this.StartPoint, point) < tolerance)
                return true;
            else
                return false;    
        }

        public override void Visit(StickConnector stickConnector)
        {
            if (stickConnector != this)
            {
                if(stickConnector.IsInTolerance(this.StartPoint, 20))
                    this.ComponentsInTolerance.Add(stickConnector);
            }
        }

        #region ICoveredByCap Members


        public void FitSnapOnCap()
        {
            SnapOnCap cap = this.SnapOnCap;
            if (cap != null)
                if (!ConnectionProvider.Connect(this, cap))
                    return;
                    //throw new Exception("Накладка не надевается.");
        }

        #endregion

        public override object Clone()
        {
            return base.Clone();
        }
    }
}
